﻿namespace BM.Tools.WebControls.MForm.Controls.DefaultTemplates
{
    using System.Collections.Generic;
    using System.Web.UI;
    using System.Xml.Schema;

    using BM.Tools.WebControls.MForm.Controls.Additions;

    /// <summary>
    /// A template that allows defining additions that may be created from xml schema.
    /// These are restrictions and identity constraints.
    /// </summary>
    public class AdditionsTemplate : ITemplate
    {        
        private Dictionary<string, IdentityConstraint> identityConstraints =
                new Dictionary<string, IdentityConstraint>();
        
        private List<KeyValuePair<string, string>> restrictionDefinitions = new List<KeyValuePair<string, string>>();

        /// <summary>
        /// Gets IdentityConstraints.
        /// </summary>
        /// <value>
        /// The identity constraints.
        /// </value>
        public Dictionary<string, IdentityConstraint> IdentityConstraints
        {
            get
            {
                return identityConstraints;
            }
        }

        /// <summary>
        /// Adds a restriction of the given type and value
        /// </summary>
        /// <param name="type">
        /// The type of the restriction.
        /// </param>
        /// <param name="value">
        /// The value.
        /// </param>
        public void AddRestriction(string type, string value)
        {
            restrictionDefinitions.Add(new KeyValuePair<string, string>(type, value));
        }

        /// <summary>
        /// Creates the additions in the given container
        /// </summary>
        /// <param name="container">
        /// The container.
        /// </param>
        public void InstantiateIn(Control container)
        {
            foreach (KeyValuePair<string, string> pair in restrictionDefinitions)
            {
                container.Controls.Add(new Restriction(pair.Key, pair.Value));
            }

            foreach (IdentityConstraint constraint in IdentityConstraints.Values)
            {
                container.Controls.Add(constraint.Clone());
            }
        }

        /// <summary>
        /// creates identity constraints out of xml schema definitions
        /// </summary>
        /// <param name="identityConstraint">
        /// The identityConstraint.
        /// </param>
        /// <returns>
        /// The created identity constraint.
        /// </returns>
        internal object CreateIdentityConstraint(XmlSchemaIdentityConstraint identityConstraint)
        {
            var key = identityConstraint as XmlSchemaKey;
            if (key != null)
            {
                return CreateKey(key);
            }

            var unique = identityConstraint as XmlSchemaUnique;
            if (unique != null)
            {
                return CreateKey(key);
            }

            var keyref = identityConstraint as XmlSchemaKeyref;
            if (keyref != null)
            {
                IdentityConstraint constraint = CreateOrGetKey(keyref.Refer.Name);
                var keyRef = CreateKeyRef(keyref);
                constraint.References.Add(keyRef);
                return keyRef;
            }

            return null;
        }

        private static string CreateSelector(XmlSchemaIdentityConstraint key)
        {
            return key.Selector.XPath + "/" + ((XmlSchemaXPath)key.Fields[0]).XPath;
        }

        private static KeyRef CreateKeyRef(XmlSchemaKeyref keyref)
        {
            return new KeyRef { Selector = CreateSelector(keyref) };
        }

        private IdentityConstraint CreateKey(XmlSchemaIdentityConstraint key)
        {
            IdentityConstraint constraint = CreateOrGetKey(key.Name);
            constraint.Selector = CreateSelector(key);
            return constraint;
        }        

        private IdentityConstraint CreateOrGetKey(string keyName)
        {
            if (!IdentityConstraints.ContainsKey(keyName))
            {
                var constraint = new IdentityConstraint
                                 {
                                         Name = keyName
                                 };
                IdentityConstraints.Add(keyName, constraint);
            }

            return IdentityConstraints[keyName];
        }
    }
}